﻿using System;
using Plugins;
using Plugins.Base;
using StockAccessors;
using StockModel;
using Utils;

namespace FullDayStrategyPlugin.Realtime
{
	internal class Engine : IDisposable
	{
		#region Attributes

		private readonly IOrderExecutor _orderExecutor;
		private readonly DecisionEngineData _engineData;
		private readonly ObservableCollection2<StockGroup> _groups;

		private DataLoader _loader;
		private DataFeeder _feeder;
		private OperationManager _operationManager;
		private VolumeCalculator _volumeCalculator;
		private DecisionEngine _decisionEngine;
		private readonly IStockDataAccessorInformation _accessorInformation;

		#endregion

		#region Properties

		public EngineSettings Settings { get; set; }
		public bool IsStarted { get; private set; }

		#endregion

		#region Events

		public event EventHandler<RealtimeNotificationEventArgs> RealtimeNotification;

		#endregion

		#region Public Methods

		public Engine(IOrderExecutor orderExecutor, ObservableCollection2<StockGroup> groups, DecisionEngineData engineData, IStockDataAccessorInformation accessorInformation)
		{
			_orderExecutor = orderExecutor;
			_groups = groups;
			_engineData = engineData;
			_accessorInformation = accessorInformation;
		}

		public void Start()
		{
			this.OnRealtimeNotification("Full Day Strategy Plugin - Started");
			this.InternalInitialize();

			_loader.StockFound += Loader_StockFound; 
			_loader.Start();
			
			this.IsStarted = true;
		}
		
		public void Stop()
		{
			_loader.Stop();
			_loader.StockFound -= Loader_StockFound; 

			this.OnRealtimeNotification("Full Day Strategy Plugin - Stopped");
			this.IsStarted = false;
		}

		protected virtual void OnRealtimeNotification(string  message)
		{
			if (this.RealtimeNotification != null)
			{
				this.RealtimeNotification(this, new RealtimeNotificationEventArgs(message));
			}
		}

		#endregion

		#region Event Handlers

		private void Loader_StockFound(object sender, StockFoundEventArgs e)
		{
			this.OnRealtimeNotification("New stock detected: " + e.Symbol);
			_operationManager.StockSymbol = e.Symbol;
		}

		private void DecisionEngine_RealtimeNotification(object sender, RealtimeNotificationEventArgs e)
		{
			this.OnRealtimeNotification(e.Message);
		}

		private void OperationManager_RealtimeNotification(object sender, RealtimeNotificationEventArgs e)
		{
			this.OnRealtimeNotification(e.Message);
		}

		#endregion

		#region Private Methods

		private void InternalInitialize()
		{
			if (_operationManager != null)
			{
				_operationManager.RealtimeNotification -= OperationManager_RealtimeNotification;
				_decisionEngine.RealtimeNotification -= DecisionEngine_RealtimeNotification;

				_feeder.Dispose();
				_decisionEngine.Dispose();
				_volumeCalculator.Dispose();
				_loader.Dispose();
			}

			_operationManager = new OperationManager(this.Settings.StockParameters, _orderExecutor, this.Settings.InvertLogic);
			_volumeCalculator = new VolumeCalculator(_operationManager, this.Settings);
			_loader = new DataLoader(_groups, this.Settings.SelectedStockName);
			_feeder = new DataFeeder(_loader.Data, this.Settings);
			_decisionEngine = new DecisionEngine(this.Settings, _operationManager, _volumeCalculator, _feeder.Data, _engineData, _accessorInformation);

			_decisionEngine.RealtimeNotification += DecisionEngine_RealtimeNotification; // Local object
			_operationManager.RealtimeNotification += OperationManager_RealtimeNotification; // Local object
		}

		#endregion

		#region IDisposable

        protected bool Disposed { get; private set; }

        private void Dispose(bool disposing)
        {
            if (!this.Disposed)
            {
                this.InternalDispose(disposing);
            }

            this.Disposed = true;
        }

        protected virtual void InternalDispose(bool disposing)
        {
            if (disposing)
            {
				if (_operationManager != null)
				{
					_operationManager.RealtimeNotification -= OperationManager_RealtimeNotification;
					_decisionEngine.RealtimeNotification -= DecisionEngine_RealtimeNotification;

					_feeder.Dispose();
					_decisionEngine.Dispose();
					_volumeCalculator.Dispose();
					_loader.Dispose();
				}
			}

            // Dispose here only unmanaged objects 
            // Don’t use managed objects here because maybe 
            // they have been finalized already
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        ~Engine()
        {
            this.Dispose(false);
        }

        #endregion
	}
}
